﻿using System;
using System.Collections.Generic;
using TallComponents.Web.Pdf.Resources;
using TallComponents.PDF.TextExtraction;
using TallComponents.PDF.Rasterizer.Configuration;
using TallComponents.PDF.Rasterizer.Diagnostics;
using System.IO;
using TallComponents.PDF.Navigation;
using System.Web.Script.Serialization;

namespace TallComponents.Web.Pdf
{
   /// <summary>
   /// Provides information about a PDF document.
   /// </summary>
   public sealed class Document : TallComponents.Web.Pdf.Annotations.INamedDestinationResolver
   {
      /// <summary>
      /// Initializes a new instance of the <see cref="Document"/> class.
      /// </summary>
      /// <param name="stream">A stream containing a valid PDF document.</param>
      /// <param name="fileName">Name of the file.</param>
      /// <param name="storageKey">The storage key.</param>
      public Document( System.IO.Stream stream, string fileName, string storageKey ) :
         this( stream, fileName, storageKey, null )
      {
      }

      /// <summary>
      /// Initializes a new instance of the <see cref="Document"/> class.
      /// </summary>
      /// <param name="stream">A stream containing a valid PDF document.</param>
      /// <param name="fileName">Name of the file.</param>
      /// <param name="storageKey">The storage key.</param>
      /// <param name="password">The password required to open this document or <c>null</c>.</param>
      public Document( System.IO.Stream stream, string fileName, string storageKey, string password) :
         this(stream, fileName, storageKey, password, DateTime.MinValue)
      {
      }


      /// <summary>
      /// Initializes a new instance of the <see cref="Document"/> class.
      /// </summary>
      /// <param name="stream">A stream containing a valid PDF document.</param>
      /// <param name="fileName">Name of the file.</param>
      /// <param name="storageKey">The storage key.</param>
      /// <param name="password">The password required to open this document or <c>null</c>.</param>
      /// <param name="lastModified">The last modified date.</param>
      public Document( System.IO.Stream stream, string fileName, string storageKey, string password, DateTime lastModified)
      {
         if (null == stream)
            throw new ArgumentNullException("stream");

         if (null != password)
         {
            _kitDocument = new TallComponents.PDF.Document(stream, password);
            if (stream.CanSeek)
               stream.Seek(0, SeekOrigin.Begin);
            _rasterizerDocument = new PDF.Rasterizer.Document(stream, password);
         }
         else
         {
            _kitDocument = new TallComponents.PDF.Document(stream);
            if (stream.CanSeek)
               stream.Seek(0, SeekOrigin.Begin);
            _rasterizerDocument = new PDF.Rasterizer.Document(stream);
         }

         if (null == _kitDocument)
            throw new ArgumentNullException("document");


         LastModified = lastModified;
         _pages = new PageCollection(this, _kitDocument.Pages);
         _bookmarks = _kitDocument.Bookmarks;
         _documentInfo = new DocumentInfo(_kitDocument.DocumentInfo.Title, _kitDocument.DocumentInfo.Subject, _kitDocument.DocumentInfo.Keywords, _kitDocument.DocumentInfo.Author, _kitDocument.DocumentInfo.Creator, storageKey, _pages.Count, fileName);

         StorageKey = storageKey;
         FileName = fileName;
      }

      /// <summary>
      /// Initializes a new instance of the <see cref="Document"/> class.
      /// </summary>
      /// <param name="document">The document.</param>
      /// <param name="fileName">Name of the file.</param>
      /// <param name="storageKey">The storage key.</param>
      internal Document(TallComponents.PDF.Document document, string fileName, string storageKey)
      {
         if (null == document)
            throw new ArgumentNullException("document");

         _kitDocument = document;
         _pages = new PageCollection(this, _kitDocument.Pages);
         _bookmarks = _kitDocument.Bookmarks;
         _documentInfo = new DocumentInfo(_kitDocument.DocumentInfo.Title, _kitDocument.DocumentInfo.Subject, _kitDocument.DocumentInfo.Keywords, _kitDocument.DocumentInfo.Author, _kitDocument.DocumentInfo.Creator, storageKey, _pages.Count, fileName);

         StorageKey = storageKey;
         FileName = fileName;
      }

      /// <summary>
      /// Gets or sets the last modified date.
      /// </summary>
      /// <value>
      /// The last modified date.
      /// </value>
      public DateTime LastModified { get; set; }

      /// <summary>
      /// Gets the file for this document.
      /// </summary>
      /// <value>The file name.</value>
      public string FileName { get; private set; }

      /// <summary>
      /// Gets the storage key.
      /// </summary>
      /// <remarks>
      /// The storage key is used to retrieve the document from the active storage provider.
      /// <para>For security reasons, the storage key is typically an encrypted value.</para>
      /// </remarks>
      /// <value>The storage key.</value>
      public string StorageKey { get; private set; }

      /// <summary>
      /// Gets the document information.
      /// </summary>
      /// <value>The document information.</value>
      public DocumentInfo DocumentInfo
      {
         get
         {
            return _documentInfo;
         }
      }

      /// <summary>
      /// Gets the collection of bookmarks in this document.
      /// </summary>
      [ScriptIgnore]
      internal BookmarkCollection Bookmarks
      {
         get
         {
            return _bookmarks;
         }
      }

      /// <summary>
      /// Gets the collection of pages in this document.
      /// </summary>
      /// <value>The page collection.</value>
      public PageCollection Pages 
      {
         get
         {
            return _pages;
         }
      }

      /// <summary>
      /// Starts a search for the specified text on the specified page.
      /// </summary>
      /// <param name="pageIndex">Index of the page.</param>
      /// <param name="text">The text to look for.</param>
      /// <param name="matchCase">if set to <c>true</c> the search is case sensitive.</param>
      /// <param name="matchWholeWord">if set to <c>true</c> only whole word matches are returned.</param>
      /// <returns>
      /// All occurences of the specified text in the document.
      /// </returns>
      /// <remarks>Searching can take some time so the text matches are returend on demand. Every time the iterator receives a call
      /// to MoveNext (for example the next iteration in a foreach loop) a search is started for the next occurrence.</remarks>
      internal IEnumerable<TextMatch> Find( int pageIndex, string text, bool matchCase, bool matchWholeWord )
      {
         if (string.IsNullOrEmpty(text))
         {
            throw new ArgumentException(Text.Document_Find_Text_must_not_be_null_or_empty, "text");
         }

         var criteria = new TextFindCriteria()
                        {
                           //Backwards = false,
                           MatchCase = matchCase,
                           MatchWholeWord = matchWholeWord,
                           Text = text
                        };

         return _kitDocument.Pages[pageIndex].Find(criteria);
      }

      /// <summary>
      /// Draws the page.
      /// </summary>
      /// <param name="index">The index.</param>
      /// <param name="graphics">The graphics.</param>
      /// <param name="renderSettings">The render settings.</param>
      /// <param name="summary">The summary.</param>
      internal void DrawPage( int index, System.Drawing.Graphics graphics, RenderSettings renderSettings, Summary summary)
      {
         _rasterizerDocument.Pages[index].Draw(graphics, renderSettings, summary);
      }

      #region INamedDestinationResolver Members

      /// <summary>
      /// Resolves the destination with the specified name.
      /// </summary>
      /// <param name="name">The name of the destination to resolve.</param>
      /// <param name="destination">The resolved destination.</param>
      /// <returns>
      /// <c>true</c> if the destination was successfully resolved, <c>false</c> otherwise.
      /// </returns>
      bool TallComponents.Web.Pdf.Annotations.INamedDestinationResolver.TryResolve( string name, out TallComponents.PDF.Navigation.Destination destination )
      {
         destination = _kitDocument.NamedDestinations[name];
         return null != destination;
      }

      #endregion

      private TallComponents.PDF.Rasterizer.Document _rasterizerDocument;
      TallComponents.PDF.Document _kitDocument;
      DocumentInfo _documentInfo;
      PageCollection _pages;
      BookmarkCollection _bookmarks;
   }
}
